package com.rc.evcharger.protocol.ylevcharger.service;

import com.alibaba.fastjson.JSONObject;
import com.rc.evcharger.common.code.YlControlCode;
import com.rc.evcharger.mapper.rc.RcSettingMapper;
import com.rc.evcharger.model.elec.ElecStationPrice;
import com.rc.evcharger.model.rc.RcSetting;
import com.rc.evcharger.protocol.ylevcharger.netty.server.YlMessageHandler;
import com.rc.mutievcharger.dto.ylevcharger.terminal.*;
import com.rc.evcharger.biz.log.ChargedProcessLogAssemble;
import com.rc.evcharger.common.constant.SocketConstant;
import com.rc.evcharger.common.constant.WebConstant;
import com.rc.evcharger.common.enums.ChargePhaseEnum;
import com.rc.evcharger.common.type.TimingDate;
import com.rc.evcharger.common.utils.DataUtil;
import com.rc.evcharger.common.utils.YlDataUtil;
import com.rc.evcharger.component.UidGenerator;
import com.rc.evcharger.mapper.member.MemberInfoMapper;
import com.rc.evcharger.model.elec.ElecGun;
import com.rc.evcharger.model.elec.ElecPile;
import com.rc.evcharger.model.member.MemberInfo;
import com.rc.evcharger.model.order.OrderComment;
import com.rc.evcharger.model.order.OrderInfo;
import com.rc.evcharger.redis.RedChargeDataService;
import com.rc.evcharger.service.elec.ElecGunService;
import com.rc.evcharger.service.elec.ElecPileService;
import com.rc.evcharger.service.elec.ElecStationPriceService;
import com.rc.evcharger.service.order.OrderCommentService;
import com.rc.evcharger.service.order.OrderInfoService;
import com.rc.evcharger.vo.charge.StartChargeReq;
import com.rc.evcharger.vo.charge.StopChargeReq;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;

/**
 * 向桩发送指令
 */
@Service
public class YlCommandServiceImpl implements YlCommandService {
    private static Logger logger = LoggerFactory.getLogger(YlCommandServiceImpl.class);

    @Autowired
    private UidGenerator uidGenerator;

    @Autowired
    private MemberInfoMapper memberInfoMapper;

    @Autowired
    private ElecGunService elecGunService;

    @Autowired
    private ElecPileService elecPileService;

    @Autowired
    private ElecStationPriceService elecStationPriceService;

    @Autowired
    private OrderInfoService orderInfoService;

    @Autowired
    private RcSettingMapper rcSettingMapper;

    @Autowired
    private ChargedProcessLogAssemble chargedProcessLogAssemble;

    @Autowired
    private OrderCommentService orderCommentService;

    @Autowired
    private RedChargeDataService redChargeDataService;

    @Autowired
    private CommandCommonService commandCommonService;

    @Override
    public Integer query_start_charge(StartChargeReq startChargeReq) {
        //充电枪验证消息
        Integer checkStartResult = commandCommonService.checkStart(startChargeReq);
        if (SocketConstant.SUCCESS != checkStartResult) {
            return checkStartResult;
        }

        ElecPile elecPile = elecPileService.selectByPrimaryKey(startChargeReq.getElecPileId());
        ElecGun elecGun = elecGunService.validGun(elecPile, startChargeReq.getGunNum());
        if (elecGun == null) {
            logger.error("该电桩{}的枪号异常，枪号为:{}", elecPile.getPileNo(), startChargeReq.getGunNum());
            return SocketConstant.EXCEPTION;
        }

        // 获取内部订单流水号
        String orderNum = uidGenerator.genChargeOrderNum(elecGun.getGunNo());
        if (StringUtils.isBlank(orderNum)) {
            logger.error("无法生成充电订单号码，枪号为:{}", elecPile.getPileNo(), startChargeReq.getGunNum());
            return SocketConstant.FAIL;
        }

        //发送充电指令
        YlStartCommandResp dataType = new YlStartCommandResp(orderNum, startChargeReq.getGunNum());


        //充电策略0:充满为止 1:时间控制充电 2:金额控制充电 3:电量控制充电
        //启动策略参数 充电策略参数 时间单位为 1 秒 金额单位为 0.01 元  电量时单位为 0.01kw
        switch (startChargeReq.getStartType()) {
            case "1":
                dataType.setChargingPolicy("01 00 00 00");
                dataType.setChargingPolicyParm(DataUtil.getValuetoHex(new BigDecimal(startChargeReq.getStartValue()), 0));
                break;
            case "2":
            case "3":
                dataType.setChargingPolicy("0" + startChargeReq.getStartType() + " 00 00 00");
                dataType.setChargingPolicyParm(DataUtil.getValuetoHex(new BigDecimal(startChargeReq.getStartValue()), 2));
                break;
            default:
                dataType.setChargingPolicy("00 00 00 00");
                dataType.setChargingPolicyParm("00 00 00 00");
        }

        //拼接桩编号的帧
        String rtuaFrame = YlDataUtil.getTerminalIdFrame(elecGun.getGunNo());
        dataType.setTerminalId(rtuaFrame);


        YlTerminalMessage message = new YlTerminalMessage(rtuaFrame, YlControlCode.CONTROL_CHARGE_START_STOP, dataType);

        boolean flag = YlMessageHandler.write(message);

        String gunNum = startChargeReq.getGunNum();
        if (gunNum.length() < 2) {// 低位补0
            gunNum = "0" + gunNum;
        }

        if (flag) {
            //保存充电结果 公众号去拉取该数据
            //redChargeDataService.putChargeResult(elecPile.getPileRtua(), elecGun.getGunNum(), memberInfo.getMobile(), 0);
            redChargeDataService.putChargeResult(elecPile.getPileRtua(), elecGun.getGunNum(), orderNum, 0);

            //保存预订单
            OrderComment orderComment = orderCommentService.insertStartCharge(startChargeReq, orderNum);

            //保存充电日志
            chargedProcessLogAssemble.assembleChargedProcessLog(ChargePhaseEnum.START, orderComment);

            return SocketConstant.SUCCESS;
        } else {
            redChargeDataService.putChargeResult(elecPile.getPileRtua(), elecGun.getGunNum(), orderNum, 1);
            return SocketConstant.FAIL;
        }
    }

    @Override
    public Integer query_stop_charge(StopChargeReq stopChargeRequest) {
        //验证码订单号是否准确
        OrderInfo orderInfo = orderInfoService.selectByOrderNum(stopChargeRequest.getOrderNum());
        if (orderInfo == null) {
            logger.error("停止充电失败,未找到对应的订单号。该订单号为：" + stopChargeRequest.getOrderNum());
            return SocketConstant.FAIL;
        }

        ElecPile pile = elecPileService.selectByPrimaryKey(orderInfo.getElecPileId());
        ElecGun gun = elecGunService.selectByPrimaryKey(orderInfo.getElecGunId());

        //手动停止则添加标签
        MemberInfo memberInfo = memberInfoMapper.selectByPrimaryKey(orderInfo.getMemberId());

        YlStopCommandResp dataType = new YlStopCommandResp(gun.getGunNum());
        //拼接桩编号的帧
        String rtuaFrame = YlDataUtil.getTerminalIdFrame(gun.getGunNo());
        dataType.setTerminalId(rtuaFrame);

        YlTerminalMessage message = new YlTerminalMessage(rtuaFrame, YlControlCode.CONTROL_CHARGE_STOP, dataType);
        boolean flag = YlMessageHandler.write(message);

        if (flag) {
            //保存充电结果
            //redChargeDataService.putChargeResult(pile.getPileRtua(), gun.getGunNum(), memberInfo.getMobile(), 0);
            redChargeDataService.putChargeResult(pile.getPileRtua(), gun.getGunNum(), orderInfo.getOrderNum(), 0);

            //记录停止方式
            redChargeDataService.putStopMode(pile.getPileRtua(), gun.getGunNum(), orderInfo.getOrderNum(), stopChargeRequest.getStopType());

            OrderComment orderComment = orderCommentService.selectByOrderNum(orderInfo.getOrderNum());
            //保存充电日志
            chargedProcessLogAssemble.assembleChargedProcessLog(ChargePhaseEnum.STOP, orderComment);

            return SocketConstant.SUCCESS;
        }

        //发送指令失败
        redChargeDataService.putChargeResult(pile.getPileRtua(), gun.getGunNum(), orderInfo.getOrderNum(), 1);

        return SocketConstant.FAIL;
    }


    private Integer checkStop(StopChargeReq stopChargeRequest) {
        OrderInfo order = orderInfoService.selectByOrderNum(stopChargeRequest.getOrderNum());
        if (order == null) {
            logger.error("停止充电失败,未找到对应的订单号。该订单号为：" + stopChargeRequest.getOrderNum());
            return SocketConstant.FAIL;
        }

        if ((SocketConstant.CON2 + "").equals(order.getStatus())) {
            logger.error("该订单号为：{},已结算，不能重复停止。", stopChargeRequest.getOrderNum());
            return SocketConstant.FAIL;
        }

        return SocketConstant.SUCCESS;
    }

    /**
     * 发送远程升级指令
     */
    public Integer sendRemoteUpdate(Long pileId, String fileName, String startType) {
        return SocketConstant.FAIL;
    }

    /**
     * 发送校时指令
     *
     * @param elecGunId
     * @return
     */
    @Override
    public int sendTimingDate(Long elecGunId) {
        ElecGun elecGun = elecGunService.selectByPrimaryKey(elecGunId);

        String terminalId = YlDataUtil.getTerminalIdFrame(elecGun.getGunNo());
        //得到标准时间
        YlTimingCommandResp dataType = new YlTimingCommandResp();
        dataType.setObligate1("00 00");
        dataType.setObligate2("00 00");
        dataType.setCmdType("01");
        dataType.setCmdAddress("02 00 00 00");
        dataType.setCmdLength("08 00");

        TimingDate timingDate = new TimingDate("setDate");
        dataType.setCmdData(timingDate.getDate());

        YlTerminalMessage message = new YlTerminalMessage(terminalId, YlControlCode.WORK_SETTING_STRING, dataType);
        boolean flag = YlMessageHandler.write(message);
        if (flag == true) {
            return SocketConstant.SUCCESS;
        }
        return SocketConstant.FAIL;
    }

    /**
     * @param pileId
     * @param type   0心跳间隔时间 1
     * @return
     */
    @Override
    public int sendIntegerParam(Long pileId, int type) {
        ElecPile elecPile = elecPileService.selectByPrimaryKey(pileId);

        String terminalId = YlDataUtil.getTerminalIdFrame(elecPile.getPileNo());
        //得到标准时间
        YlWorkSettingCommandResp dataType = new YlWorkSettingCommandResp();

        RcSetting rcSetting = rcSettingMapper.selectByPrimaryKey(1l);

        //0心跳间隔时间
        if (type == 0) {
            dataType.setObligate1("00 00");
            dataType.setObligate2("00 00");
            dataType.setParamType("01");
            dataType.setParamAddress("15 00 00 00");
            dataType.setParamCount("01");
            dataType.setParamByteCount("04");
            String data = Integer.toHexString(rcSetting.getHearbeatTime());
            if (data.length() == 1) {
                data = "0" + data;
            }
            dataType.setData(data);
        } else if (type == 1) {
            dataType.setObligate1("00 00");
            dataType.setObligate2("00 00");
            dataType.setParamType("01");
            dataType.setParamAddress("16 00 00 00");
            dataType.setParamCount("01");
            dataType.setParamByteCount("04");
            String data = Integer.toHexString(rcSetting.getHearbeatOvertime());
            if (data.length() == 1) {
                data = "0" + data;
            }
            dataType.setData(data);
        }


        YlTerminalMessage message = new YlTerminalMessage(terminalId, YlControlCode.WORK_SETTING_STRING, dataType);
        boolean flag = YlMessageHandler.write(message);
        if (flag == true) {
            return SocketConstant.SUCCESS;
        }
        return SocketConstant.FAIL;
    }

    @Override
    public int sendResetPile(Long pileId) {

        ElecPile elecPile = elecPileService.selectByPrimaryKey(pileId);

        YlStopCommandResp resetPile = new YlStopCommandResp();
        resetPile.setObligate1("00 00");
        resetPile.setObligate2(resetPile.getObligate1());
        resetPile.setGunNum("01");
        resetPile.setCmdAddress("0B 00 00 00");
        resetPile.setCmdCount("01");
        resetPile.setCmdLength("04 00");
        resetPile.setCmdParam("55 00 00 00");

        //拼接桩编号的帧
        String rtuaFrame = YlDataUtil.getTerminalIdFrame(elecPile.getPileNo());
        resetPile.setTerminalId(rtuaFrame);

        YlTerminalMessage message = new YlTerminalMessage(rtuaFrame, YlControlCode.CONTROL_CHARGE_STOP, resetPile);
        boolean flag = YlMessageHandler.write(message);
        if (flag == true) {
            logger.info("下发重启设备指令成功,对应设备号:{}", elecPile.getPileNo());
            return SocketConstant.SUCCESS;
        }
        logger.info("下发重启设备指令失败,对应设备号:{}", elecPile.getPileNo());
        return SocketConstant.FAIL;
    }


    /**
     * 费率下发
     **/
    @Override
    public int sendPrice(Long pileId) {
        ElecPile elecPile = elecPileService.selectByPrimaryKey(pileId);

        List<ElecGun> elecGuns = elecGunService.listByElecPileId(pileId);

        for (ElecGun elecGun : elecGuns) {
//            String terminalId = YlDataUtil.getTerminalIdFrame(elecPile.getPileNo());
            String terminalId = YlDataUtil.getTerminalIdFrame(elecGun.getGunNo());

            List<ElecStationPrice> elecStationPrices = elecStationPriceService.listByStationId(elecPile.getElecStationId());

            YlPriceRequestCommondResp dataType = null;
            Map<String, String> map = new HashMap<>();

            Calendar calendar = Calendar.getInstance();
            for (int i = 0; i < 12; i++) {
                for (int j = i; j < elecStationPrices.size(); j++) {

                    int k = i + 1;
                    ElecStationPrice elecStationPrice = elecStationPrices.get(j);
                    calendar.setTime(elecStationPrice.getBeginTime());


                    String beginHourHex = Integer.toHexString(calendar.get(Calendar.HOUR_OF_DAY) == 23 ? 24 : calendar.get(Calendar.HOUR_OF_DAY)).toUpperCase();
                    if (beginHourHex.length() == 1) {
                        beginHourHex = "0" + beginHourHex;
                    }
                    map.put("beginHour" + k, beginHourHex);

                    String beginMinuteHex = Integer.toHexString(calendar.get(Calendar.MINUTE)).toUpperCase();
                    if (beginMinuteHex.length() == 1) {
                        beginMinuteHex = "0" + beginMinuteHex;
                    }
                    map.put("beginMinute" + k, beginMinuteHex);

                    //结束时间
                    calendar.setTime(elecStationPrice.getEndTime());
                    //29,59分时加一分钟
                    if (calendar.get(Calendar.MINUTE) == 59 || calendar.get(Calendar.MINUTE) == 29) {
                        calendar.add(Calendar.MINUTE, 1);
                    }
                    String endHourHex = Integer.toHexString(calendar.get(Calendar.HOUR_OF_DAY) == 00 ? 24 : calendar.get(Calendar.HOUR_OF_DAY)).toUpperCase();
                    if (endHourHex.length() == 1) {
                        endHourHex = "0" + endHourHex;
                    }
                    map.put("endHour" + k, endHourHex);

                    String endMinuteHex = Integer.toHexString(calendar.get(Calendar.MINUTE)).toUpperCase();
                    if (endMinuteHex.length() == 1) {
                        endMinuteHex = "0" + endMinuteHex;
                    }
                    map.put("endMinute" + k, endMinuteHex);

                    //总价取两位小数
                    String s = DataUtil.toHexFrameByAccount(elecStationPrice.getElecPrice().add(elecStationPrice.getServicePrice()), 2);
                    map.put("priceRatio" + k, s);

                    break;
                }

                JSONObject jsonObject = new JSONObject();
                jsonObject.putAll(map);
                dataType = jsonObject.toJavaObject(YlPriceRequestCommondResp.class);
            }

            YlTerminalMessage message = new YlTerminalMessage(terminalId, YlControlCode.RESP_PRICE_REQUEST, dataType);
            boolean flag = YlMessageHandler.write(message);
            if (flag == false) {
                logger.error("下发电价失败，对应枪编号:{}", elecGun.getGunNo(), elecGun.getPileRuta());
                return SocketConstant.FAIL;
            }
            logger.info("下发电价成功，对应枪编号:{}", elecGun.getGunNo(), elecGun.getPileRuta());
        }

        logger.info("下发电价成功，对应桩编号:{}", elecPile.getPileNo());
        return SocketConstant.SUCCESS;
    }

    /**
     * 设置主屏幕二维码
     *
     * @param pileId
     * @return
     */
    @Override
    public int sendQCode(Long pileId) {
        ElecPile elecPile = elecPileService.selectByPrimaryKey(pileId);

        String terminalId = YlDataUtil.getTerminalIdFrame(elecPile.getPileNo());

        //构造主界面二维码  hlht://115344000021.914401153/      前缀+桩编号+瑞晨后缀
        //永联双枪双TCP链接,相当于单枪
        String qcode = WebConstant.prefix + elecPile.getPileNo() + "." + elecPile.getTenantCode() + "/";
        String cmdQcode = YlDataUtil.dataToASCIIFrame(qcode, 256);

        YlQCodeComandResp dataType = new YlQCodeComandResp(cmdQcode);

        YlTerminalMessage message = new YlTerminalMessage(terminalId, YlControlCode.WORK_SETTING_STRING, dataType);
        boolean flag = YlMessageHandler.write(message);
        if (flag == true) {
            return SocketConstant.SUCCESS;
        }
        return SocketConstant.FAIL;
    }


    /**
     * 字符串转16进制
     *
     * @param str
     * @return
     */
    private String stringToHex(String str, int length) {
        StringBuffer sb = new StringBuffer();
        int remainder = length - str.length();
        if (remainder < 0) {
            return null;
        }
        for (int i = 0; i < str.length(); i++) {
            int dec = str.charAt(i);
            String hex = Integer.toHexString(dec);
            if (hex.length() < 2) {
                hex = "0" + hex;
            }
            sb.append(hex).append(" ");
        }
        if (remainder > 0) {
            for (int i = 0; i < remainder; i++) {
                sb.append("00").append(" ");
            }
        }
        return sb.toString().trim();
    }


    public static void main(String[] args) {
        System.out.println(String.format("%08d", 123));
        try {
            System.out.println(InetAddress.getByName("v0.ftp.upyun.com").getHostAddress());
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }

        Map<String, String> map = new HashMap<>();
        map.put("beginHour1", "1234");
        JSONObject jsonObject = new JSONObject();
        jsonObject.putAll(map);
        YlPriceRequestCommondResp dataType = jsonObject.toJavaObject(YlPriceRequestCommondResp.class);
        System.out.println(dataType.getBeginHour1() + " " + dataType.getEndHour1());

    }
}
